use crate::{id, times};

#[allow(unused_macros)]
macro_rules! pipe2 {
    ($value: expr) => {
        $value
    };

    // fun call with args
    ($value: expr => $fun:ident($($args: expr),*) $($tail:tt)*) => {{
        pipe2!($fun($($args,)* $value) $($tail)*)
    }};

    // director call
    ($value: expr => $fun:ident $($tail: tt)*) => {
        pipe2!($fun($value) $($tail)*)
    };

    // type convert
    ($value: expr => (as $type:ty) => $($tail: tt)*) => {
        pipe2!($value as $type=> $($tail)*)
    };
    ($value: expr => (&) $($tail: tt)*) => {
        pipe2!(&$value $($tail)*)
    };
    ($value: expr => & $($tail: tt)*) => {
        pipe2!(&$value $($tail)*)
    };
    ($value: expr => (&mut) $($tail: tt)*) => {
        pipe2!(&mut $value $($tail)*)
    };

    // method
    ($value: expr => .$method: ident ($($args: expr),*) $($tail: tt)*) => {
        pipe2!($value.$method($($args,)*) $($tail)*)
    };

    ($value: expr => .$method: ident $($tail: tt)*) => {
        pipe2!($value.$method() $($tail)*)
    };

    // closure
    ($value: expr => |$arg: ident : $ty:ty|$body:block $($tail: tt)*) => {{
        let res = {|$arg: $ty|$body}($value);
        pipe2!(res $($tail)*)
    }};
    ($value: expr => |$arg: ident|$body:block $($tail: tt)*) => {{
        let res = {|$arg|$body}($value);
        pipe2!(res $($tail)*)
    }};
    ($value: expr => {$closure: expr} $($tail: tt)*) => {{
        let res = $closure($value);
        pipe2!(res $($tail)*)
    }};
}

#[test]
fn test_pipe0() {
    let a = pipe2!(1 => id => times(2, 3) => times(3, 2) => (as i64) => |x: i64| { -x } => .abs);
    let mut b = "sss".to_string();
    pipe2!(&mut b => .push('c'));
    assert_eq!(a, 36);
}

#[test]
fn test_pipe1() {
    let mut arr = vec![1, 2, 3];
    let new_arr: Vec<i32> = pipe2!(arr => (&mut) => .iter => .map(|x| x + 1) => .collect);
}
